package cn.geminis.data.jpa.graphql.utils;

import graphql.schema.*;

import javax.persistence.metamodel.*;

import static graphql.schema.GraphQLFieldDefinition.newFieldDefinition;

/**
 * @author puddi
 */
public class JpaAttributeUtils {

    private static GraphQLType getAttributeType(Attribute<?, ?> attribute) {
        var persistentType = attribute.getPersistentAttributeType();
        if (Attribute.PersistentAttributeType.BASIC.equals(persistentType)) {
            return TypeUtils.getScalarType(attribute.getJavaType());

        } else if (Attribute.PersistentAttributeType.ONE_TO_MANY.equals(persistentType)
                || Attribute.PersistentAttributeType.MANY_TO_MANY.equals(persistentType)) {
            var foreignType = (EntityType<?>) ((PluralAttribute<?, ?, ?>) attribute).getElementType();
            return new GraphQLList(new GraphQLTypeReference(foreignType.getName()));

        } else if (Attribute.PersistentAttributeType.MANY_TO_ONE.equals(persistentType)
                || Attribute.PersistentAttributeType.ONE_TO_ONE.equals(persistentType)) {
            var foreignType = (EntityType<?>) ((SingularAttribute<?, ?>) attribute).getType();
            return new GraphQLTypeReference(foreignType.getName());

        } else if (Attribute.PersistentAttributeType.ELEMENT_COLLECTION.equals(persistentType)) {
            var foreignType = ((PluralAttribute<?, ?, ?>) attribute).getElementType();
            return new GraphQLList(TypeUtils.getScalarType(foreignType.getJavaType()));

        } else if (Attribute.PersistentAttributeType.EMBEDDED.equals(persistentType)) {
            var embeddableType = (EmbeddableType<?>) ((SingularAttribute<?, ?>) attribute).getType();
            return new GraphQLTypeReference(embeddableType.getJavaType().getSimpleName());
        }

        var declaringType = attribute.getDeclaringType().getJavaType().getName();
        var declaringMember = attribute.getJavaMember().getName();

        throw new UnsupportedOperationException(
                "Attribute could not be mapped to GraphQL: field '" + declaringMember + "' of entity class '" + declaringType + "'");
    }

    public static GraphQLFieldDefinition getOutputField(Attribute<?, ?> attribute) {
        return newFieldDefinition()
                .name(attribute.getName())
                .type((GraphQLOutputType) getAttributeType(attribute))
                .build();
    }

    public static GraphQLArgument getInputField(Attribute<?, ?> attribute) {
        return GraphQLArgument.newArgument()
                .name(attribute.getName())
                .type((GraphQLInputType) getAttributeType(attribute))
                .build();
    }

}
